from datetime import datetime
from bert_serving.client import BertClient
import numpy as np
import sys
import logging
import logging.handlers
from termcolor import colored
import configparser
import requests
import json
import base64
import struct
from util.RedisUtil import redisClient
from util.ThreadPool import threadPool

rsClient = redisClient.rs
executor = threadPool.executor
doc_vecs_path = 'vecs.npy'


class Knowledge:
    # 构建客户端
    bc = any
    doc_vecs = None
    condinates = []

    def __init__(self):
        self.bc = BertClient(ip='10.39.65.192')
        # 初始化类时从文件加载模型数据
        try:
            self.doc_vecs = np.load(doc_vecs_path)
        except:
            print("加载模型向量失败！")
            # sys.exit(0)

    def train(self, data, taskId):
        # 从候选集condinates 中选出与sentence_a 最相近的句子
        # self.condinates = data
        # with open("question.txt", encoding="utf-8") as file:
        #     for it in file.readlines():
        #         it = it.strip("\n")
        #         if it.find("##") != -1:
        #             sub = it.split("##")
        #             condinates.extend(sub)
        #         else:
        #             condinates.append(it)
        # 处理训练数据
        keys = []
        values = []
        for it in data:
            self.condinates.append(it.get("content"))
            keys.append(it.get("content"))
            values.append(it.get("label"))
            # rsClient.set(it.get("content"), it.get("label"))
        # redis使用管道批量设置键值对
        # todo 此操作与训练操作可同时进行，可改为多线程
        pipe = rsClient.pipeline()
        for i in range(0, len(keys)):
            pipe.set(keys[i], values[i])
        pipe.execute()
        # print('训练数据长度：', self.condinates)
        print('开始训练')
        start = datetime.now()
        try:
            self.doc_vecs = self.bc.encode(self.condinates)
        except:
            self.notice(taskId, '0')
        # 训练结果存redis的字符串
        rsClient.set("condinates", json.dumps(self.condinates, ensure_ascii=False))
        print("---------type-------------", type(self.doc_vecs))
        # 使用文件保存数组
        np.save(doc_vecs_path, self.doc_vecs)
        # self.redis_save("modelStr", self.doc_vecs)
        # redis.set("modelStr", self.doc_vecs)
        # print("redis存的modelStr为：", redis.get("modelStr"))
        # print('训练后的模型数据如下：', self.doc_vecs)
        end = datetime.now()
        print("训练结束花费时间：", (end - start).seconds, '秒')
        # 训练完成后通知调用接口
        self.notice(taskId, '1')

    def recognise(self, text):
        # 从候选集condinates 中选出与sentence_a 最相近的句子
        query_vec = self.bc.encode([text])[0]
        # compute normalized dot product as score
        topk = 500
        # print('模型数据：', doc_vecs)
        # score = np.sum(query_vec * doc_vecs, axis=1) / np.linalg.norm(doc_vecs, axis=1)
        # score = np.dot(query_vec, doc_vecs) / (np.linalg.norm(query_vec) * np.linalg.norm(doc_vecs))
        #  识别结果集
        scoreList = []
        # modelStr = redis.get("modelStr")
        # doc_vecs = self.redis_read("modelStr")
        for doc in self.doc_vecs:
            cosine_similarity = np.dot(query_vec, doc) / (np.linalg.norm(query_vec) * np.linalg.norm(doc))
            scoreList.append(cosine_similarity)

        topk_idx = np.argsort(scoreList)[::-1][:topk]
        print("猜你可能的问题score: ", scoreList[0:20])
        print("top_idx", topk_idx[0:20])

        self.condinates = json.loads(rsClient.get("condinates"))

        data = []
        idList = []
        for idx in topk_idx:
            #  print('> %s\t%s' % (score[idx], condinates[idx]))
            sentence = self.condinates[idx]
            score = str(scoreList[idx])
            # print(score, colored(sentence, 'yellow'))
            id = rsClient.get(sentence)
            if id not in idList:
                idList.append(id)
                data.append({"id": id, "score": score, "sentence": sentence})
        print(data[0:20])
        return data[0:20]

    def notice(self, taskId, success):
        print("开始通知...")
        fallbackUrl = rsClient.get("fallbackUrl")
        fallbackUrl = fallbackUrl + "/" + str(taskId) + "?success=" + str(success)
        print("fallbackUrl：", fallbackUrl)
        # 取得训练数据
        try:
            rq = requests.post(fallbackUrl, timeout=200)
            if rq.raise_for_status():
                print("%s  服务器错误!", fallbackUrl)
        except:
            print("%s 服务未启动!", fallbackUrl)
        else:
            print("通知成功！")

    def getDocVecs(self):
        if self.doc_vecs is None:
            self.doc_vecs = np.load(doc_vecs_path)
        return self.doc_vecs
